package org.xx.armory.ofs;

import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

import static java.util.Base64.getEncoder;
import static java.util.regex.Pattern.compile;
import static org.apache.commons.lang3.StringUtils.join;
import static org.xx.armory.commons.Validators.greaterThan;
import static org.xx.armory.commons.Validators.notBlank;

public final class FileObjectUtils {
    private static final String HASH_ALGORITHM = "SHA-1";
    private static final Pattern HASH_NORM_FILTER = compile("[/*%=+-]");

    private FileObjectUtils() {
    }

    /**
     * 将掩码形式的文件属性解析为文件属性集合。
     *
     * @param attribute
     *         待解析的掩码形式的文件属性。
     * @return 解析结果。
     * @see #combineAttributes(Collection)
     */
    public static Set<FileAttribute> parseAttributes(
            long attribute
    ) {
        final Set<FileAttribute> ret = EnumSet.noneOf(FileAttribute.class);
        for (final FileAttribute attr : FileAttribute.values()) {
            if ((attr.value() & attribute) != 0L) {
                ret.add(attr);
            }
        }
        return ret;
    }

    /**
     * 将文件属性集合组合为掩码形式的文件属性。
     *
     * @param attributes
     *         文件属性集合。
     * @return 组合后的掩码，如果参数 {@code attributes} 是{@code null}则返回0。
     * @see #parseAttributes(long)
     */
    public static long combineAttributes(
            Collection<FileAttribute> attributes
    ) {
        if (attributes == null) {
            return 0L;
        }

        if (attributes.contains(FileAttribute.NORMAL) && attributes.size() > 1) {
            // NORMAL 只能单独使用。
            throw new IllegalArgumentException("illegal attributes: " + join(attributes, ","));
        }

        int ret = 0;
        for (final FileAttribute attr : attributes) {
            ret |= attr.value();
        }
        return ret;
    }

    /**
     * 计算文件的哈希值。
     *
     * @param id
     *         文件对象的ID。
     * @param name
     *         文件名。
     * @return 计算结果。
     * @throws IllegalArgumentException
     *         如果参数 {@code id}小于等于{@code 0} 或者 {@code name}是{@code null}或者只包含空白字符。
     */
    public static String hashFile(
            long id,
            String name
    ) {
        greaterThan(id, "id", 0L);
        name = notBlank(name, "name").trim();

        // 首先计算32位整数形式的哈希值。
        final int fileIdHash = (int) id;
        final int nameHash = name.hashCode();
        final int a = (17 * 31 + fileIdHash) * 31 + nameHash;

        // 然后转化为4个字节组成的数组，低位在前。
        final byte[] b = new byte[]{
                (byte) (a & 0xFF), (byte) ((a >> 8) & 0xFF),
                (byte) ((a >> 16) & 0xFF), (byte) ((a >> 24) & 0xFF)};

        // 对字节数组计算哈希值。
        final byte[] c;
        try {
            c = MessageDigest.getInstance(HASH_ALGORITHM).digest(b);
        } catch (GeneralSecurityException ex) {
            throw new UnsupportedOperationException("cannot create MessageDigest", ex);
        }

        // 将计算结果进行Base64编码，并截取[2, 8)位字符，形成6个字符长的哈希码。
        // 并将哈希码中的特殊字符替换为下划线(_)。
        return HASH_NORM_FILTER
                .matcher(getEncoder().encodeToString(c).substring(2, 8))
                .replaceAll("_");
    }

    /**
     * 创建一个新的UUID。
     *
     * @return 新的UUID。
     */
    public static UUID createUuid() {
        return UUID.randomUUID();
    }
}
